#include <string>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "snda_ecs_sdk.h"
#include "snda_ecs_http_util.h"
#include "snda_ecs_constants.h"
#include "snda_ecs_common_util.h"

#include "gcstor.h"
#include "config.h"

using std::string;

int isdir(const char* path) {
    struct stat s_buf;

    if (stat(path, &s_buf))
        return 0;

    return S_ISDIR(s_buf.st_mode);
}

GCStorage::GCStorage() : connected(false), handler(0) {

}

int GCStorage::connect() {
	if (!connected) {
		handler = snda_ecs_init_handler();
		if (!handler) {
			return -1;
		}
		connected = true;
		return 0;
	} else {
		return -1;
	}
}

void GCStorage::disconnect() {
	if (connected) {
		snda_ecs_release_handler(handler);
		connected = false;
	}
}

int GCStorage::get(const char* pathname,char* buffer,size_t size) {
	string bucket = Config::Get("snda-bucket"),
		key = Config::Get("snda-key"),
		secret = Config::Get("snda-secret"),
		region = Config::Get("snda-region");
	string localfile = "sndatmp/";

	SNDAECSErrorCode retcode = SNDA_ECS_ERROR;

	for (int i = 0; i < 12; i++) {
		localfile = localfile + (char)('a' + rand()%26);
	}

    int ssl = 0;
    SNDAECSFollowLocation followlocation = SNDA_ECS_FOLLOW_LOCATION;
    int count = 0;
	long maxredirects = 3;
    SNDAECSByteRange* byterangeptr = 0;
    FILE* writefd = 0, *readfd = 0;
    SNDAECSResult* ret = 0;

    ret = snda_ecs_init_result();
	writefd = fopen(localfile.c_str(), "wb");
	retcode = snda_ecs_get_object(handler, key.c_str(), secret.c_str(), bucket.c_str(), pathname, byterangeptr,
			snda_ecs_write_fun, writefd, region.c_str(), ssl,  followlocation, maxredirects,
			ret);
	fclose(writefd);

	if (SNDA_ECS_SUCCESS != retcode) {
		snda_ecs_release_result(ret);
		return -1;
	}

	readfd = fopen(localfile.c_str(), "rb");
	if (!readfd) {
		snda_ecs_release_result(ret);
		return -1;
	}
	count = fread(buffer, sizeof(char), size, readfd);
	fclose(readfd);
	snda_ecs_release_result(ret);
	return count;
}

int GCStorage::put(const char* pathname,const char* buffer,size_t size) {
	string bucket = Config::Get("snda-bucket"),
		key = Config::Get("snda-key"),
		secret = Config::Get("snda-secret"),
		region = Config::Get("snda-region");
	string localfile = "sndatmp/",
		contenttype = "binary/octet-stream";

	SNDAECSErrorCode retcode = SNDA_ECS_ERROR;

	for (int i = 0; i < 12; i++) {
		localfile = localfile + (char)('a' + rand()%26);
	}

    int ssl = 0;
    SNDAECSUserObjectMeta* objectmeta = 0;
    FILE* fd = 0;
    SNDAECSResult* ret = 0;

    ret = snda_ecs_init_result();

    fd = fopen(localfile.c_str(), "wb");

    size_t done = 0;

	while (done < size) {
	    size_t written = fwrite((char*)buffer + done * sizeof(char), sizeof(char), size - done, fd);
	    done += written;
	    if (done < size) {
	        /* Not all has been written. Some kind of error may have occurred. */
	        if (ferror((FILE*)fd)) {
	            return -1;
	        }
	    }
	}

	fclose(fd);

	fd = fopen(localfile.c_str(), "rb");
	retcode = snda_ecs_put_object(handler, key.c_str(), secret.c_str(), bucket.c_str(), pathname,
			snda_ecs_put_object_body, fd, size,
			objectmeta, region.c_str(), ssl,
			ret);
    fclose(fd);

    if (SNDA_ECS_SUCCESS != retcode) {
		snda_ecs_release_result(ret);
		return -1;
	}
	snda_ecs_release_result(ret);
	return 0;
}

int GCStorage::del(const char* pathname) {
	string bucket = Config::Get("snda-bucket"),
		key = Config::Get("snda-key"),
		secret = Config::Get("snda-secret"),
		region = Config::Get("snda-region");

	SNDAECSErrorCode retcode = SNDA_ECS_ERROR;
	int ssl = 0;
	SNDAECSFollowLocation followlocation = SNDA_ECS_FOLLOW_LOCATION;
	long maxredirects = 3;
	SNDAECSResult* ret = 0;

    ret = snda_ecs_init_result();

	retcode = snda_ecs_delete_object(handler, key.c_str(), secret.c_str(), bucket.c_str(), pathname, region.c_str(), ssl, followlocation, maxredirects, ret);

	if (SNDA_ECS_SUCCESS != retcode) {
		snda_ecs_release_result(ret);
		return -1;
	}
	snda_ecs_release_result(ret);
	return 0;
}

int GCStorage::mkdir(const char* path,const char* name) {
	string str;
	str = str + path + name + "/";
	char buffer[4] = "dir";

	return put(str.c_str(), buffer, 3);
}

int GCStorage::rmdir(const char* path,const char* name) {
	string str;
	str = str + path + name + "/";

	return del(str.c_str());
}

struct NetFile* GCStorage::lsdir(const char* path,OUT int* size /* numbner of struct NetFile */) {
	string bucket = Config::Get("snda-bucket"),
		key = Config::Get("snda-key"),
		secret = Config::Get("snda-secret"),
		region = Config::Get("snda-region");
	const char* marker = 0;
	const char* delimiter = 0;

	SNDAECSErrorCode retcode = SNDA_ECS_ERROR;
	int ssl = 0;
	int maxkeys = 1000;
	SNDAECSFollowLocation followlocation = SNDA_ECS_FOLLOW_LOCATION;
	long maxredirects = 3;
	SNDAECSResult* ret = 0;

    ret = snda_ecs_init_result();

	retcode = snda_ecs_get_bucket(handler, key.c_str(), secret.c_str(), bucket.c_str(), path, marker, delimiter, maxkeys, region.c_str(), ssl, followlocation, maxredirects, ret);

	if (retcode == SNDA_ECS_SUCCESS && ret->serverresponse->httpcode == 200) {
		SNDAECSGetBucketResultContent* content = snda_ecs_to_get_bucket_result(ret);
		int count=0, cursor=0, pathlen = 0;
		pathlen = strlen(path);
		NetFile *files=0;
		if (content) {
			if (content->objects) {
				SNDAECSObjectContent* object = content->objects;
				while (object) {
					char *name = object->objectname;
					char *p = name + pathlen;
					int namelen = strlen(name);
					char *q = strchr(p, '/');
					if (q == NULL && *p != '\0') { //file
						count++;
					} else if (q == name + namelen - 1) {//folder
						count++;
					}
					object = object->next;
				}
				if (count > 0) files = (NetFile*)malloc(sizeof(NetFile) * count);
				object = content->objects;
				while (object) {
					char *name = object->objectname;
					char *p = name + pathlen;
					int namelen = strlen(name);
					char *q = strchr(p, '/');
					if (q == NULL && *p != '\0') { //file
						strcpy(files[cursor].fileName, p);
						files[cursor].folder = 0;
						files[cursor].size = object->size;
						cursor++;
					} else if (q == name + namelen - 1) {//folder
						strcpy(files[cursor].fileName, p);
						files[cursor].fileName[strlen(p) - 1] ='\0';
						files[cursor].folder = 1;
						files[cursor].size = 0;
						cursor++;
					}
					object = object->next;
				}
			}
		}
		snda_ecs_release_get_bucket_result_content(content);
		*size = count;
		if (files == NULL) files = new NetFile;//sentinel
		return files;
	} else {
		snda_ecs_release_result(ret);
		*size = 0;
		return NULL;
	}
}

int GCStorage::move(const char* oldPath,const char* newPath) {
	string bucket = Config::Get("snda-bucket"),
		key = Config::Get("snda-key"),
		secret = Config::Get("snda-secret"),
		region = Config::Get("snda-region");

    int ssl = 0;
    SNDAECSUserObjectMeta* objectmeta = 0;
    SNDAECSErrorCode retcode = SNDA_ECS_ERROR;
	SNDAECSResult* ret = 0;

    ret = snda_ecs_init_result();

	retcode = snda_ecs_copy_object(handler, key.c_str(),
			secret.c_str(), bucket.c_str(), newPath, region.c_str(), bucket.c_str(),
			oldPath, objectmeta, ssl, ret);

	if (SNDA_ECS_SUCCESS != retcode) {
		snda_ecs_release_result(ret);
		return -1;
	}

	return del(oldPath);
}

int GCStorage::freespace() {
	return -1;
}

int GCStorage::init() {
	snda_ecs_global_init();
	srand(time(NULL));

    DIR* dp;
    struct dirent* ep;
    char p_buf[512] = {0};
    dp = opendir("sndatmp/");
    while ((ep = readdir(dp)) != NULL) {
        sprintf(p_buf, "%s/%s", "sndatmp/", ep->d_name);
        if (!isdir(p_buf))
            unlink(p_buf);
    }
    closedir(dp);

	return 0;
}

int GCStorage::destroy() {
	snda_ecs_global_uninit();
	return 0;
}
